package cn.ryanpenn.javaservicewrapper.samples.filewatchservice;

import java.io.*;
import java.nio.channels.FileChannel;
import java.nio.file.*;
import java.util.Properties;
import java.util.concurrent.TimeUnit;

/**
 * FileWatcher
 */
public class FileWatcher {

    private static final String TARGET_PATH = "fileWatcher.target.path";
    private static final String SOURCE_PATH = "fileWatcher.source.path";
    private static final String EVENTS = "fileWatcher.events";

    String targetPath = "";
    String sourcePath = "";
    String[] events = {};
    boolean[] event_supports = {false, false, false};

    String getJarPath() {

        java.net.URL url = FileWatcher.class.getProtectionDomain().getCodeSource().getLocation();
        String filePath = null;
        try {
            filePath = java.net.URLDecoder.decode(url.getPath(), "utf-8");
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (filePath.endsWith(".jar"))
            filePath = filePath.substring(0, filePath.lastIndexOf("/") + 1);
        java.io.File file = new java.io.File(filePath);
        filePath = file.getAbsolutePath();
        return filePath;
    }

    void loadConf() throws IOException {

        InputStream propStream;

        String propFile = "conf.properties";
        File file = new File(getJarPath() + "/../conf/" + propFile);
        if (file.exists()){
            propStream = new FileInputStream(file);
        }else{
            propStream = FileWatcher.class.getClassLoader().getResourceAsStream(propFile);
        }

        // 加载配置
        Properties prop = new Properties();
        prop.load(new InputStreamReader(propStream, "UTF-8"));

        // 监控路径
        targetPath = prop.getProperty(TARGET_PATH);
        sourcePath = prop.getProperty(SOURCE_PATH);
        events = prop.getProperty(EVENTS).split(",");

        for (String e : events) {
            if ("CREATE".equalsIgnoreCase(e))
                event_supports[0] = true;
            else if ("MODIFY".equalsIgnoreCase(e))
                event_supports[1] = true;
            else if ("DELETE".equalsIgnoreCase(e))
                event_supports[2] = true;
        }
    }

    void setUpWatch() throws Exception {
        // 获取文件系统的WatchService对象
        WatchService watchService = FileSystems.getDefault().newWatchService();
        Path path = Paths.get(sourcePath);
        path.register(watchService, StandardWatchEventKinds.ENTRY_CREATE,
                StandardWatchEventKinds.ENTRY_MODIFY,
                StandardWatchEventKinds.ENTRY_DELETE);

        while (true) {
            final WatchKey watchKey = watchService.poll(1, TimeUnit.HOURS); // watchService.take();
            if (watchKey != null) {
                watchKey.pollEvents().stream().forEach(event -> {
                    if (event.kind() == StandardWatchEventKinds.ENTRY_CREATE
                            && event_supports[0]) {
                        System.out.printf("create: %s/%s \n copy to: %s/%s\n",
                                sourcePath, event.context(), targetPath, event.context());
                        copyFile(event.context().toString());
                    } else if (event.kind() == StandardWatchEventKinds.ENTRY_MODIFY
                            && event_supports[1]) {
                        System.out.printf("modify: %s/%s \n merge to: %s/%s\n",
                                sourcePath, event.context(), targetPath, event.context());
                        copyFile(event.context().toString());
                    } else if (event.kind() == StandardWatchEventKinds.ENTRY_DELETE
                            && event_supports[2]) {
                        System.out.printf("delete: %s/%s \n remove to: %s/%s\n",
                                sourcePath, event.context(), targetPath, event.context());
                        deleteFile(event.context().toString());
                    }
                });

                // 重设WatchKey
                boolean valid = watchKey.reset();
                // 如果重设失败，退出监听
                if (!valid) {
                    break;
                }
            }
        }
    }


    void copyFile(String fileName) {
        String source = sourcePath + "/" + fileName;
        String dest = targetPath + "/" + fileName;

        File file = new File(dest);
        if (!file.exists()) {
            try {
                file.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        try (FileInputStream fis = new FileInputStream(source);
             FileOutputStream fos = new FileOutputStream(file);
             FileChannel inputChannel = fis.getChannel();
             FileChannel outputChannel = fos.getChannel()
        ) {
            outputChannel.transferFrom(inputChannel, 0, inputChannel.size());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    void deleteFile(String fileName) {
        String dest = targetPath + "/" + fileName;

        File file = new File(dest);
        if (file.exists())
            file.delete();
    }

    void start() throws Exception {
        loadConf();
        setUpWatch();
    }

    // FileWatcherApp.bat install
    public static void main(String[] args) throws Exception {
        new FileWatcher().start();
    }

}
